{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial: Gli strumenti di base per il deep learning privato\n",
    "\n",
    "Benvenuto! In questo primo tutorial cercheremo di spiegarti come funziona PySyft. PySyft nasce come soluzione alle problematiche di privacy e sicurezza dei dati nei sistemi di machine learning, fornendo strumenti al supporto della decentralizzazione dei modelli e la privatizzazione dei dati. Questa serie di notebook si configura come una guida passo dopo  passo per venire a conoscenza di nuovi sistemi e strumenti al servizio del deep learning su dati, modelli encriptati o privati in maniera decentralizzata. \n",
    "\n",
    "\n",
    "\n",
    "**Scopo:** In questi tutorial, non ci limiteremo esclusivamente a trattare in maniera teorica tecniche di decentralizzazione o crittografia dei dati, ma utilizzeremo PySyft per decentralizzare l'intero ecosistema attorno ai dati. Ciò comprende sia i database in cui i dati vengono archiviati, sia i modelli di reti neurali impiegate. Man mano che verranno implementate nuove funzionalità per PySyft, questi notebook verranno estesi con nuovi tutorial per spiegarne il loro funzionamento. \n",
    "\n",
    "Autore:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "Traduzione ad opera di:\n",
    "- Daniele Gadler - Twitter [@DanieleGadler](https://twitter.com/DanieleGadler)\n",
    "- Andrea Provino \n",
    "\n",
    "## Struttura:\n",
    "\n",
    "- Parte 1: Gli strumenti di base per il deep learning privato\n",
    "\n",
    "\n",
    "## Perchè dovresti seguire questo tutorial?\n",
    "\n",
    "**1) Vantaggio competitivo nella ricerca di un lavoro**. Negli ultimi 20 anni, lo sviluppo tecnologico ha prodotto una mole di dati impressionante, riducendo progressivamente il tempo di raddoppio: quello necessario a produrre una quantità di dati pari a quella creata dall’inizio della storia umana al momento della misurazione.\n",
    "\n",
    "Le nuove regolazioni sui dati, come il [GDPR](https://eugdpr.org/), hanno introdotto regolamenti sempre più stringenti sul passaggio e sulla manipolazione delle informazioni degli utent, motivo per cui oggigiorno le aziende devono fare particolare  attenzione all'utilizzo e all'analisi dei dati utente.\n",
    "\n",
    "Queste regolamentazioni impongono delle grosse limitazioni ai data scientist, i queli si trovano a dover lavorare su piccoli dataset contenenti poche informazioni utili di alto valore. Apprendendo strumenti privacy-preserving per il deep learning, come PySyft, puoi restare anche tu al passo anche con i tempi e ottenerne un vantaggio per la tua carriera.\n",
    "\n",
    "**2) Opportunità imprenditoriali**.  Esistono moltissimi problemi sociali che potrebbero essere risolti o attenuati sfruttando tecnologie di deep learning. Al momento, il principale ostacolo per la risoluzione di questi problemi è l’assenza o l'impossibilità di accedere ai dati in conformità con le regolazioni esistenti. Difatti, i sistemi di machine learning hanno bisogno di accedere a molte informazioni sensibili e private su persone con problemi di salute, la cui privacy andrebbe preservata ad ogni costo.\n",
    "\n",
    "Imparare a utilizzare sistemi per il deep learning privato equivale quindi a fornire nuove opportunità di business alle startup, altrimenti non realizzabili con sistemi di machine learning tradizionali. \n",
    "\n",
    "\n",
    "**3) Bene Sociale** - Il deep learning può essere usato per risolvere una grande varietà di problemi del  mondo reale. Ora, creare sistemi di Deep Learning basandosi sulle informazioni personali, significa creare sistemi di Deep Learning *per le persone*.\n",
    "\n",
    "Imparare ad implementare il deep learning su dati che non si posseggono è molto più che un semplice trampolino per la propria carriera o un' opportunità imprenditoriale: è la possibilità di risolvere alcuni dei problemi più personali e importanti nella vita delle persone - e farlo su una vasta scala.\n",
    "\n",
    "\n",
    "## Come posso supportare il progetto \"PySyft\"?\n",
    "\n",
    "- Dai una stella a PySyft PySyft su GitHub! - [https://github.com/OpenMined/PySyft](https://github.com/OpenMined/PySyft)\n",
    "- Realizza un video Youtube in cui spieghi il funzionamento di questo notebook!\n",
    "\n",
    "\n",
    "... ok ... cominciamo!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte - 1: Prerequisiti\n",
    "\n",
    "- Conoscenza di PyTorch - altrimenti, consulta il course al seguente indirizzo http://fast.ai\n",
    "- Leggi il paper di PySyft https://arxiv.org/pdf/1811.04017.pdf! Questo paper ti fornirà un'idea generale sull'architettura di PySyft, il che ti aiuterà a comprendere meglio il funzionamento di PySyft."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 0: Setup\n",
    "\n",
    "Per cominciare, assicurati di avere le dependencies di PySyft installate. Le istruzioni di installazione sono contenute le file README.MD di PySyft. Per la maggior parte delle persone questo è:\n",
    "\n",
    "- Installa Python 3.6+\n",
    "- Installa PyTorch 1.3\n",
    "- Esegui un clone della repository di PySyft (git clone https://github.com/OpenMined/PySyft.git)\n",
    "- cd PySyft\n",
    "- pip install -r pip-dep/requirements.txt\n",
    "- pip install -r pip-dep/requirements_udacity.txt\n",
    "- python setup.py install udacity\n",
    "- python setup.py test\n",
    "\n",
    "Se riscontri un errore nella procedura di installazione (o se un test case fallisce) - innanzitutto consulta il [README](https://github.com/OpenMined/PySyft.git) per aiuto con l'installazione ed eventualmente apri un Issue su Github. In alternative, prova a sentire se qualcuno nel canale #beginner di Slack ti può dare una mano! [slack.openmined.org](http://slack.openmined.org/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Esegui questa cella per verificare se PySyft è stato installato correttamente\n",
    "import sys\n",
    "\n",
    "import torch\n",
    "from torch.nn import Parameter\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n",
    "\n",
    "torch.tensor([1,2,3,4,5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Se sei riuscito ad eseguire questa cella con successo, PySyft è stato installato correttamente! Complimenti!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 1: Gli strumenti di base per Data Science privata e decentralizzata\n",
    "\n",
    "\n",
    "Quindi, ti starai chiedendo  - Come facciamo ad eseguire il training di modelli su dati a cui non abbiamo accesso?\n",
    "\n",
    "Be, la risposta è in realtà molto semplice. Se sei abituato a lavorare in PyTorch, allora sei abitiato a lavorare con oggetti torch.Tensor, come i seguenti:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = x + x\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ovviamente, è importante usare questi \"eleganti\" tensori, ma ciò richiede che i dati risiedano sulla tua macchina locale. Ed è qui che comincia il nostro viaggio.\n",
    "\n",
    "\n",
    "# Section 1.1 - Sending Tensors to Bob's Machine\n",
    "\n",
    "Mentre normalmente eseguiremo operazioni di machine learning sulla macchine dove i dati risiedono, ora vorremo eseguire questo tipo di computazioni su un' **altra** macchina. Nello specifico, non possiamo più presupporre che i dati risiedano sulla nostra macchina locale. \n",
    "\n",
    "Pertanto, invece di usare tensori Torch, ora lavoreremo con **puntatori** a tensori. Per prima cosa, creiamo una macchina fittizia, appartenente ad una persona fittizia, che chiameremo bob."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id=\"bob\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Supponiamo che la macchine di Bob sia su un altro pianeta, volendo anche su Marte! But, al momento la sua macchina è vuota. Ora creeremo dei dati da mandare a Bob e cercheremo di capire come funzionano i puntatoroi!\n",
    "\n",
    "Let's say Bob's machine is on another planet - perhaps on Mars! But, at the moment the machine is empty. Let's create some data so that we can send it to Bob and learn about pointers!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = torch.tensor([1,1,1,1,1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "E ora - mandiamo i nostri tensori a Bob!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr = x.send(bob)\n",
    "y_ptr = y.send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BOOM! Ora Bob ha due tensori! Non mi credi? Guarda te stesso!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = x_ptr + x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ora fai attenzione. Quando abbiamo invocato il comando `x.send(bob)`, questo ha restituito un nuovo oggetto che abbiamo chiamato `x_ptr`. Questo è il nostro primo *puntatore* a un tensore. I puntatori a tensori non contengono dati loro stesso, ma contengono semplicemente dei metadati riguardo ad un tensore, con dati immagazzinati su un'altra macchina. Lo scopo di questi tensori è di fornirci un'API intuitiva per dire all'altra macchina di eseguire delle funzioni usando questo tensore. Diamo un'occhiata al tipo di metadati contenuti nei puntatori."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ecco i metadati contenuti in un puntatore!\n",
    "\n",
    "Vi sono due attributi specifici dei puntatori:\n",
    "\n",
    "- `x_ptr.location : bob`, la location rappresenta un riferimento al \"luogo\" a cui il puntatore punta\n",
    "- `x_ptr.id_at_location : <random integer>`, l'id dove il tensore è immagazzinato. \n",
    "\n",
    "I metadati vengono stampati nel seguente formato: `<id_at_location>@<location>`\n",
    "\n",
    "I metadati contengono altri due attributi generici: \n",
    "- `x_ptr.id : <random integer>`, l'id del nostro puntatore al tensore, assegnato in maniera causale\n",
    "- `x_ptr.owner : \"me\"`, il worker che possiede il puntatore al tensore, in tal caso il worker locale chiamato \"me\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob == x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.id_at_location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ti starai forse chiedendo perchè il worker locale che possiede il puntatore è anche un VirtualWorker, nonostante non lo avessimo creato.\n",
    "\n",
    "La cosa interessante è che, mentre noi avevamo un oggetto VirtualWorker per bob, noi (di default) abbiamo un Virtual Worker anche per noi stessi. Questo worker viene creato automaticamente quando invochiamo il comando `hook = sy.TorchHook()`, cosicchè tu non debba crearlo te stesso. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "me = sy.local_worker\n",
    "me"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "me == x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finalmente, nella stessa maniera in cui possiamo invocare .send() su un tensore, possiamo invocare .get() su un puntatore ad un tensore per \"estrarre\" il tensore originale dal puntatore."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "E come puoi vedere, Bob non ha più nessun tensore!!! Tutti i tensori sono ritornati sulla nostra macchina locale! "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sezione 1.2 - Utilizzo di Puntatori a Tensori\n",
    "\n",
    "Quindi, abbiamo ormai capito come mandare e ricevere tensori da Bob, ma sicuramente questo non è Deep Learning! Vorremmo riuscire ad eseguire _operazioni_ su tensori remoti, proprio come eseguimo operazioni su tensori locali. Per nostra fortuna, i puntatori ai tensori rendono questo processo piuttosto facile! Puoi semplicemente usare puntatori a tensori, nella stessa maniera in cui useresti tensori PyTorch normali. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5]).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = x + y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "E voilà!\n",
    "\n",
    "Dietro questa banale operazione, in realtà è successo qualcosa di molto potente! Invece di eseguire un'operazione di addizione localmente, un comando è stato serializzato e mandato a Bob; Bob ha quindi eseguito la computazione, creato un tensore z, e ci ha quindi restituito un puntatore a z.\n",
    "\n",
    "Se invochiamo .get() sul puntatore, il risultato ci verrà quindi restituito sulla nostra macchina locale!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Funzioni Torch\n",
    "\n",
    "Questa API è stata estesa a tutte le funzioni di PyTorch!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = torch.add(x,y)\n",
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Variabili (inclusa la backpropagation!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5.], requires_grad=True).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1.], requires_grad=True).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = (x + y).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Quindi, come puoi vedere, la API di PySyft è piuttosto flessibile e può essere utilizzata per eseguire quasi qualsiasi operazione che eseguiresti in PyTorch su *dati remoti*. Queste funzionalità pongeono le fondamenta per l'implementazione di protocolli privacy-preserving più avanzati, tra cui Federated Learning, Secure Multi-Party Computation, e Differential Privacy!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Congratulazioni!!! - Unisciti alla Community!\n",
    "\n",
    "Congratulazioni per aver completato questo notebook tutorial! Se ti è piaciuto e vorresti unirti al movimento per la tutela della privacy, la proprietà decentralizzata dell'IA, eccoti alcune possibilità:\n",
    "\n",
    "### Dai una stella a PySyft su GitHub\n",
    "\n",
    "Il modo più semplice per supportare la nostra community è dando una stella alle repository Github di OpenMined! Questo aiuta ad aumentare la consapevolezza e l'attenzione verso i  fantastici strumenti che stiamo plasmando.\n",
    "\n",
    "\n",
    "- [Dai una stella a PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Unisciti a Slack!\n",
    "\n",
    "Puoi tenerti aggiornato sugli ultimi progressi unendoti alla nostra comunity! Puoi farlo compilando il modulo all'indirizzo [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Unisciti ad un progetto di codice!\n",
    "\n",
    "Il modo migliore per contribuire alla nostra community è diventare un contributor di codice! In qualsiasi momento puoi andare sugli pagina degli Issues di PySyft, e filtrare per \"Progetti\". In questa maniera, visualizzerai tutti i ticket di alto livello, fornendoti un'idea dei progetti a cui potresti unirti! \n",
    "\n",
    "Se non vuoi unirti ad un progetto, ma vorresti comunque contribuire con del codice, puoi semplicemente cercare dei mini-progetti \"Una-Tantum\", caratterizzati dal tag \"good first issue\".\n",
    "\n",
    "- [Progetti PySyft](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Ticket 'Good First Issue'](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Donate\n",
    "\n",
    "Se non hai tempo di contribuire al nostro codice, ma vorresti comunque dare una mano, puoi diventare un sostenitore del progetto PySyft sul nostro Open Collective. Tutte le donazioni ricevute verranno devolute al web hosting and ad altre spese della community, tipo hackathons e incontri di PySyft.\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
